import { Service } from 'egg';
import { IJenkinsTaskReportModel, JenkinsTaskReport } from '../model/jenkinsTaskModel';
import { PageModel } from '../model/responseModel';
import { IApplicationUserModel, UserInfoEntity } from '../model/user';
import jenkinsapi = require('jenkins-api');
import { flatten } from 'lodash';
import { IJenkinsPlanReportModel, JenkinsPlanReport } from '../model/jenkinsPlanModel';
import { MD5 } from 'object-hash';
import { UserRoleEnum } from "@/constants/enum.types";
/**
 * jenkins api调用
 * @export
 * @class JenkinsService
 * @extends {Service}
 */
export default class JenkinsService extends Service {
    jenkinsToken: string = '';
    /** 获取jenkins token */
    private async getJenkinsTokenUrl() {
        if (!this.jenkinsToken) {
            const userInfo: IApplicationUserModel | null = await this.ctx.model.User.findById(this.ctx.state.user.userInfo.id);
            this.jenkinsToken = userInfo && userInfo.jToken || '';
        }
        return this.config.jenkinsHost.replace('token', this.jenkinsToken);
    }
    /** 根据api名称和参数调用jenkins api */
    public async common(params: {
        /** 要执行的api */
        apiKey: string;
        /** api相关参数 */
        options: any[];
        /** jenkins用户token */
        token?: string;
    }) {
        try {
            let jenkins;
            const jenkinsHost: string = this.config.jenkinsHost!;
            /* 有传入token时直接初始化，没有则查询当前登录用户的token后再初始化 */
            if (params.token) {
                /** 初始化jenkins api */
                jenkins = jenkinsapi.init(jenkinsHost.replace('token', params.token));
            } else {
                jenkins = jenkinsapi.init(await this.getJenkinsTokenUrl());
            }

            const res = await new Promise<any>((resolve, reject) => {
                /** 执行api */
                jenkins[params.apiKey](...params.options, (err, data) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(data);
                    }
                });
            }).catch((error: string) => {
                /** 添加日志并向上抛出错误信息 */
                this.logger.error(error);
                throw error;
            });
            return res;
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 获取当前构建队列 */
    public async getBuildQueue() {
        const jenkinsUrl = await this.getJenkinsTokenUrl();
        const ajaxBuildQueueUrl = `${jenkinsUrl}/ajaxBuildQueue`;
        const res = await this.ctx.curl<{data: Buffer}>(ajaxBuildQueueUrl, { headers: {
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        }});
        return res && res.data && res.data.toString()
        .replace(/href=["|'](.*?)["|']/g, '')
        .replace(/onclick=["|'](.*?)["|']/g, '')
        .replace(/src=["|'](.*?)["|']/g, '');
    }

    /** 获取当前构建构建执行状态 */
    public async getExecutors() {
        const jenkinsUrl = await this.getJenkinsTokenUrl();
        const ajaxExecutorsUrl = `${jenkinsUrl}/ajaxExecutors`;
        const res = await this.ctx.curl<{data: Buffer}>(ajaxExecutorsUrl, { headers: {
            'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
            'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        }});
        return res && res.data && res.data.toString()
        .replace(/href=["|'](.*?)["|']/g, '')
        .replace(/onclick=["|'](.*?)["|']/g, '')
        .replace(/src=["|'](.*?)["|']/g, '');
    }

    /** 获取构建进度日志 */
    public async getPogressiveHtml(jobName: string, buildNumber: string, start: number = 0) {
        const jenkinsUrl = await this.getJenkinsTokenUrl();
        const ajaxProgressiveHtmlUrl = `${jenkinsUrl}/job/${jobName}/${buildNumber}/logText/progressiveHtml`;
        const res = await this.ctx.curl<{data: Buffer, headers: any}>(ajaxProgressiveHtmlUrl, {
            headers: {
                'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
            },
            data: {
                start,
            },
        });
        if (res) {
            const size = res.headers['x-text-size'];
            const hasMoreData = res.headers['x-more-data'] ? true : false;
            return {
                size: size && Number(size) || 0,
                hasMoreData,
                html: res.data && res.data.toString(),
            };
        } else {
            return '';
        }
    }

    /** 查询项目列表 */
    public async queryList(params: {
        /** 当前页 */
        pageIndex: number;
        /** 每页条数 */
        pageSize: number;
        /** 名称 */
        name: string;
        /** 描述 */
        description: string;
        /** 项目 */
        project: string;
        /** 创建时间区间 */
        createTime: [string, string];
        /** 修改时间区间 */
        updateTime: [string, string];
    }): Promise<PageModel<IJenkinsTaskReportModel>> {
        try {
            const userInfo: UserInfoEntity = this.ctx.state.user ? this.ctx.state.user.userInfo : null;
            const condition = {
                $match: {
                    ...params.name.trim() && { name: { $regex: params.name } },
                    ...params.project.trim() && { project: { $regex: params.project } },
                    ...params.description.trim() && { description: { $regex: params.description } },
                    ...params.createTime.length && {
                        createTime: {
                            $gt: new Date(params.createTime[0]).getTime(),
                            $lt: new Date(params.createTime[1]).getTime() + 86400000,
                        },
                    },
                    ...params.updateTime.length && {
                        updateTime: {
                            $gt: new Date(params.updateTime[0]).getTime(),
                            $lt: new Date(params.updateTime[1]).getTime() + 86400000,
                        },
                    },
                    ...userInfo.roleNo < UserRoleEnum.dev_ops ? {} : { authority: { $ne: '2' } },
                },
            };
            const list: IJenkinsTaskReportModel[] = await this.ctx.model.JenkinsTaskModel.aggregate([
                {
                    ...condition,
                },
                {
                    $sort: {
                        createTime: -1,
                    },
                },
                {
                    $skip: (params.pageIndex - 1) * params.pageSize,
                },
                {
                    $limit: params.pageSize,
                },
            ]);
            const total: number = await this.ctx.model.JenkinsTaskModel.find(condition.$match).count();
            return new PageModel<IJenkinsTaskReportModel>({
                list,
                pageIndex: params.pageIndex || 1,
                pageSize: params.pageSize || 10,
                total,
            });
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 根据id查询项目详情 */
    public async detail(id: string): Promise<IJenkinsTaskReportModel | null> {
        try {
            return await this.ctx.model.JenkinsTaskModel.findById(id);
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 根据id查询删除项目 */
    public async delete(id: string): Promise<IJenkinsTaskReportModel | null> {
        try {
            return await this.ctx.model.JenkinsTaskModel.findByIdAndRemove(id);
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 新增or修改项目 */
    public async createOrModify(params: Partial<JenkinsTaskReport> & {id?: string}) {
        /** 根据jobList的中共各项的参数和名称生成唯一ID并储存 */
        if (params.jobList) {
            params.jobList = params.jobList.map((item, index) => {
                return {
                    ...item,
                    id: MD5(`${item.name}${MD5(item.params)}${index}`),
                };
            });
        }
        /** 计划名称唯一性校验 */
        if (await this.ctx.model.JenkinsTaskModel.findOne({
            name: params.name,
            _id: { $ne: params.id },
        })) {
            throw `名称为【${params.name}】的项目已存在`;
        }
        /** 项目名称非空校验 */
        if (params.name === void 0 || params.name === null || params.name.trim() === '') {
            throw '计划名称不能为空';
        }
        if (!params.id) {
            /** 新增 */
            try {
                const res = await this.ctx.model.JenkinsTaskModel.find({}, {}, {
                    limit: 1,
                    sort: {
                        createTime: -1,
                    },
                });
                return await this.ctx.model.JenkinsTaskModel.create({
                    ...params,
                    taskNo: res && res[0].taskNo + 1 || 1,
                } as JenkinsTaskReport);
            } catch (error) {
                this.logger.error(error);
                throw error;
            }
        } else {
            /** 修改 */
            try {
                const res = await this.ctx.model.JenkinsTaskModel.findByIdAndUpdate(params.id, {
                    ...params,
                });
                if (res) {
                    return await this.ctx.model.JenkinsTaskModel.findById(params.id);
                }
            } catch (error) {
                this.logger.error(error);
                throw error;
            }
        }
    }

    /** 项目合并 */
    public async merge(idList: string[]) {
        try {
            const resList: IJenkinsTaskReportModel[] = [];
            await Promise.all(idList.map(async id => {
                const res = await this.ctx.model.JenkinsTaskModel.findByIdAndDelete(id);
                if (res) {
                    resList.push(res);
                }
            }));
            if (resList.length === 0) {
                throw '合并失败';
            }
            return await this.ctx.model.JenkinsTaskModel.create({
                authority: resList[0].authority || '1',
                jobList: flatten(resList.map(item => item.jobList)),
                name: resList[0].name,
                description: resList[0].name,
            } as JenkinsTaskReport);
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 查询计划列表 */
    public async queryPlanList(params: {
        /** 当前页 */
        pageIndex: number;
        /** 每页条数 */
        pageSize: number;
        /** 名称 */
        name: string;
        /** 状态 */
        status: number;
        /** 执行时间区间 */
        executionTime: [string, string];
        /** 创建时间区间 */
        createTime: [string, string];
        /** 修改时间区间 */
        updateTime: [string, string];
    }): Promise<PageModel<IJenkinsTaskReportModel>> {
        try {
            /* const userInfo: UserInfoEntity = this.ctx.state.user ? this.ctx.state.user.userInfo : null; */
            const condition = {
                $match: {
                    ...params.name.trim() && { name: { $regex: params.name } },
                    ...params.executionTime.length && {
                        executionTime: {
                            $gt: new Date(params.executionTime[0]).getTime(),
                            $lt: new Date(params.executionTime[1]).getTime() + 86400000,
                        },
                    },
                    ...params.createTime.length && {
                        createTime: {
                            $gt: new Date(params.createTime[0]).getTime(),
                            $lt: new Date(params.createTime[1]).getTime() + 86400000,
                        },
                    },
                    ...params.updateTime.length && {
                        updateTime: {
                            $gt: new Date(params.updateTime[0]).getTime(),
                            $lt: new Date(params.updateTime[1]).getTime() + 86400000,
                        },
                    },
                    ...params.status === 0 ? {} : { status: params.status },
                },
            };
            const list: IJenkinsTaskReportModel[] = await this.ctx.model.JenkinsPlanModel.aggregate([
                {
                    ...condition,
                },
                {
                    $sort: {
                        createTime: -1,
                    },
                },
                {
                    $skip: (params.pageIndex - 1) * params.pageSize,
                },
                {
                    $limit: params.pageSize,
                },
            ]);
            const total: number = await this.ctx.model.JenkinsPlanModel.find(condition.$match).count();
            return new PageModel<IJenkinsTaskReportModel>({
                list,
                pageIndex: params.pageIndex || 1,
                pageSize: params.pageSize || 10,
                total,
            });
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 新增or修改计划 */
    public async createOrModifyPlan(params: Partial<JenkinsPlanReport> & {id?: string}) {
        /** 计划名称唯一性校验 */
        if (await this.ctx.model.JenkinsPlanModel.findOne({
            name: params.name,
            _id: { $ne: params.id },
        })) {
            throw `名称为【${params.name}】的计划已存在`;
        }
        /** 项目名称非空校验 */
        if (params.name === void 0 || params.name === null || params.name.trim() === '') {
            throw '计划名称不能为空';
        }
        if (!params.id) {
            /** 新增 */
            try {
                return await this.ctx.model.JenkinsPlanModel.create({
                    ...params,
                } as JenkinsPlanReport);
            } catch (error) {
                this.logger.error(error);
                throw error;
            }
        } else {
            /** 修改 */
            try {
                const res = await this.ctx.model.JenkinsPlanModel.findByIdAndUpdate(params.id, {
                    ...params,
                });
                if (res) {
                    return await this.ctx.model.JenkinsPlanModel.findById(params.id);
                }
            } catch (error) {
                this.logger.error(error);
                throw error;
            }
        }
    }

    /** 根据id查询计划详情 */
    public async planDetail(id: string): Promise<IJenkinsPlanReportModel | null> {
        try {
            return await this.ctx.model.JenkinsPlanModel.findById(id);
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 根据id查询删除项目 */
    public async planDelete(id: string): Promise<IJenkinsPlanReportModel | null> {
        try {
            return await this.ctx.model.JenkinsPlanModel.findByIdAndRemove(id);
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }

    /** 修改当前计划状态 */
    public async changePlanStatus(id: string, status: number): Promise<IJenkinsPlanReportModel | null> {
        try {
            return await this.ctx.model.JenkinsPlanModel.findByIdAndUpdate(id, {
                status,
            });
        } catch (error) {
            this.logger.error(error);
            throw error;
        }
    }
}
